/**
 * Object representing Flickr collections and sets
 * @param {Flickr.Tree} api JSON response from Flickr API
 * @param {Set} [previewSet] Optional Flickr set that was loaded and shown while tree was loading
 * @constructor
 * @see http://www.flickr.com/services/api/flickr.collections.getTree.html
 */
function Tree(api, previewSet)
{
	"use strict";

	/** @type {Tree} */
	var _tree = this;
	/** @type {Number} */
	var i;
	/**
	 * Root collections
	 * @type {Array.<Collection>}
	 */
	this.list = [];
	/**
	 * Hashes for quick lookup
	 * @type {Object.<Collection>}
	 */
	this.collections = {};
	/**
	 * Flat reference to all collection sets for simplified lookup
	 * @type {Array.<Set>}
	 */
	this.sets = [];

	function init()
	{
		// cache set if already loaded
		if (previewSet) { addSet(previewSet); }
		// add root collections
		for (var i = 0; i < api.collection.length; i++) { addCollection(api.collection[i]); }

		api = null;
	}

	/**
	 * Set having ID
	 * @param {String} id Flickr set ID
	 * @return {Set}
	 */
	this.setWithID = function(id)
	{
		return getSet(function(s) { return s.id == id; });
	};

	/**
	 * Add Flickr collection to the tree
	 * @param {Collection|Flickr.Collection} collection
	 */
	function addCollection(collection)
	{
		/** @type {int} */
		var i;
		/** @type {Collection} */
		var c;

		if (collection instanceof Collection)
		{
			c = collection;
		}
		else
		{
			// implies root-level collection
			// constructor recurses through child collections and sets
			c = new Collection(collection, _tree, true);
			_tree.list.push(c);
		}
		_tree.collections[c.id] = c;	// add hash reference

		for (i in c.sets) 			{ addSet(c.sets[i]); }
		for (i in c.collections) 	{ addCollection(c.collections[i]); }
	}

	/**
	 * Add set to tree if not already a member
	 * @param {Set} set
	 */
	function addSet(set)
	{
		if (_tree.setWithID(set.id) == null) { _tree.sets.push(set); }
	}

	/**
	 * Find set matching function
	 * @param {Function(SetObject)} match Method that returns boolean
	 * @return {Set}
	 */
	function getSet(match)
	{
		for (var i = 0; i < _tree.sets.length; i++)
		{
			if (match(_tree.sets[i])) { return _tree.sets[i]; }
		}
		return null;
	}

	init();
}